package database

import (
	"gitee.com/amoyx/cloud-provider/pkg/cloud/tencent/models"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
	qredis "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/redis/v20180412"
	"strconv"
)

type RedisGreeter interface {
	Redis(region string) RedisInterface
}

type RedisInterface interface {
	CreatePostpaidInstance(args models.RedisRequest) (result models.CreateResponseParams, err error)
	CreatePrepaidInstance(args models.RedisRequest) (result models.CreateResponseParams, err error)
	Get(id string) (result models.RedisResponse, err error)
	ListByVpcId(vpcId string) (list []models.RedisResponse, err error)
	ListByName(name string) (list []models.RedisResponse, err error)
	ListAll() (list []models.RedisResponse, err error)
	ClearInstanceData(id string, passwd string) (result models.CreateResponseParams, err error)
	DestroyPostpaidInstance(id string) (result models.CreateResponseParams, err error)
	DestroyPrepaidInstance(id string) (result models.CreateResponseParams, err error)
	DeleteInstance(id string) (err error)
	QueryTaskInfo(taskId uint64) (result models.TaskDetail, err error)
	ResetPassword(id string, passwd string) (err error)
	InquiryPrepaidPrice(args models.RedisRequest) (result models.ItemPrice, err error)
	InquiryPostpaidPrice(args models.RedisRequest) (result models.ItemPrice, err error)
	DisableWanAddr(id string) (result models.TaskDetail, err error)
	EnableWanAddr(id string) (result models.TaskDetail, err error)
	QueryOrderDealInfo(dealId string) (list []models.TradeDealDetail, err error)
	RecoveryInstance(id string) (err error)
	UpgradeInstance(id string, args models.RedisRequest) (err error)
}

type redis struct {
	region string
	db     *Database
	client *qredis.Client
}

func newRedis(db *Database, region string) RedisInterface {
	c := &redis{
		region: region,
		db:     db,
	}
	cpf := profile.NewClientProfile()
	cpf.HttpProfile.Endpoint = string(RedisEndPoint)
	client, _ := qredis.NewClient(c.db.credential, c.region, cpf)
	c.client = client
	return c
}

// CreatePrepaidInstance 创建包年包月的redis实例
func (r *redis) CreatePrepaidInstance(args models.RedisRequest) (result models.CreateResponseParams, err error) {
	return r.create(1, args)
}

// CreatePostpaidInstance 创建按量付费的redis实例
func (r *redis) CreatePostpaidInstance(args models.RedisRequest) (result models.CreateResponseParams, err error) {
	return r.create(0, args)
}

func (r *redis) create(chargeType int64, args models.RedisRequest) (result models.CreateResponseParams, err error) {
	request := qredis.NewCreateInstancesRequest()
	request.TypeId = common.Uint64Ptr(args.TypeId)
	request.MemSize = common.Uint64Ptr(args.MemSize)
	request.GoodsNum = common.Uint64Ptr(args.GoodsNum)
	if chargeType == 1 {
		request.Period = common.Uint64Ptr(args.Period)
	} else {
		request.Period = common.Uint64Ptr(1)
	}
	request.BillingMode = common.Int64Ptr(chargeType)
	request.Password = common.StringPtr(args.Password)
	request.VpcId = common.StringPtr(args.VpcId)
	request.SubnetId = common.StringPtr(args.SubnetId)
	request.AutoRenew = common.Uint64Ptr(1)
	request.SecurityGroupIdList = common.StringPtrs(args.SecurityGroupIds)
	if args.VPort < 1000 {
		args.VPort = 6379
	}
	request.VPort = common.Uint64Ptr(args.VPort)
	if r.isClusterType(args.TypeId) {
		request.RedisShardNum = common.Int64Ptr(args.RedisShardNum)
	}
	request.RedisReplicasNum = common.Int64Ptr(args.RedisReplicasNum)
	request.InstanceName = common.StringPtr(args.InstanceName)
	request.ZoneName = common.StringPtr(args.ZoneName)
	request.ProductVersion = common.StringPtr(args.ProductVersion)
	//request.ReplicasReadonly = common.BoolPtr(false)
	//request.NoAuth = common.BoolPtr(true)
	response, err := r.client.CreateInstances(request)
	if err != nil {
		return
	}
	res := response.Response
	result.RequestId = *res.RequestId
	for _, v := range res.InstanceIds {
		result.InstanceSet = append(result.InstanceSet, *v)
	}
	result.TaskId = append(result.TaskId, *res.DealId)
	return
}

// EnableWanAddr 开启redis实例公网IP
func (r *redis) EnableWanAddr(id string) (result models.TaskDetail, err error) {
	request := qredis.NewAllocateWanAddressRequest()
	request.InstanceId = common.StringPtr(id)
	response, err := r.client.AllocateWanAddress(request)
	if err != nil {
		return
	}
	res := response.Response
	result.TaskStatus = *res.WanStatus
	result.JobId = *res.FlowId
	result.AsyncRequestId = *res.RequestId
	return
}

// DisableWanAddr 关闭redis实例公网IP
func (r *redis) DisableWanAddr(id string) (result models.TaskDetail, err error) {
	request := qredis.NewReleaseWanAddressRequest()
	request.InstanceId = common.StringPtr(id)
	response, err := r.client.ReleaseWanAddress(request)
	if err != nil {
		return
	}
	res := response.Response
	result.TaskStatus = *res.WanStatus
	result.JobId = *res.FlowId
	result.AsyncRequestId = *res.RequestId
	return
}

// RecoveryInstance 解除redis实例隔离
func (r *redis) RecoveryInstance(id string) (err error) {
	request := qredis.NewStartupInstanceRequest()
	request.InstanceId = common.StringPtr(id)
	_, err = r.client.StartupInstance(request)
	return
}

func (r *redis) isClusterType(typeId uint64) bool {
	cluster := map[uint64]bool{
		2:  false, // 2.8标准
		3:  false, // 3.2标准
		4:  true,  // 3.2集群
		6:  false, // 4.0标准
		7:  true,  // 4.0集群
		8:  false, // 5.0标准
		9:  true,  // 5.0集群
		15: false, // 6.2标准
		16: true,  // 6.2集群
	}
	if val, ok := cluster[typeId]; val && ok {
		return true
	}
	return false
}

// ClearInstanceData 清除redis实例数据
func (r *redis) ClearInstanceData(id string, passwd string) (result models.CreateResponseParams, err error) {
	request := qredis.NewClearInstanceRequest()
	request.InstanceId = common.StringPtr(id)
	request.Password = common.StringPtr(passwd)
	response, err := r.client.ClearInstance(request)
	if err != nil {
		return
	}
	result.RequestId = *response.Response.RequestId
	taskid := strconv.FormatInt(*response.Response.TaskId, 10)
	result.TaskId = append(result.TaskId, taskid)
	return
}

// DestroyPostpaidInstance 销毁按量计费redis实例
func (r *redis) DestroyPostpaidInstance(id string) (result models.CreateResponseParams, err error) {
	request := qredis.NewDestroyPostpaidInstanceRequest()
	request.InstanceId = common.StringPtr(id)
	response, err := r.client.DestroyPostpaidInstance(request)
	if err != nil {
		return
	}
	result.RequestId = *response.Response.RequestId
	taskid := strconv.FormatInt(*response.Response.TaskId, 10)
	result.TaskId = append(result.TaskId, taskid)
	return
}

// DestroyPrepaidInstance 销毁包年包月redis实例
func (r *redis) DestroyPrepaidInstance(id string) (result models.CreateResponseParams, err error) {
	request := qredis.NewDestroyPrepaidInstanceRequest()
	request.InstanceId = common.StringPtr(id)
	response, err := r.client.DestroyPrepaidInstance(request)
	if err != nil {
		return
	}
	result.RequestId = *response.Response.RequestId
	result.TaskId = append(result.TaskId, *response.Response.DealId)
	return
}

// DeleteInstance 回收站Redis实例下线
func (r *redis) DeleteInstance(id string) (err error) {
	request := qredis.NewCleanUpInstanceRequest()
	request.InstanceId = common.StringPtr(id)
	_, err = r.client.CleanUpInstance(request)
	return
}

func (r *redis) QueryTaskInfo(taskId uint64) (result models.TaskDetail, err error) {
	request := qredis.NewDescribeTaskInfoRequest()
	request.TaskId = common.Uint64Ptr(taskId)
	response, err := r.client.DescribeTaskInfo(request)
	if err != nil {
		return
	}
	res := response.Response
	result.TaskStatus = *res.Status
	result.TaskType = *res.TaskType
	result.Message = *res.TaskMessage
	result.StartTime = *res.StartTime
	result.AsyncRequestId = *res.RequestId
	return
}

func (r *redis) QueryOrderDealInfo(dealId string) (list []models.TradeDealDetail, err error) {
	request := qredis.NewDescribeInstanceDealDetailRequest()
	request.DealIds = common.StringPtrs([]string{dealId})
	response, err := r.client.DescribeInstanceDealDetail(request)
	if err != nil {
		return
	}
	for _, row := range response.Response.DealDetails {
		var ids []string
		for _, id := range row.InstanceIds {
			ids = append(ids, *id)
		}
		list = append(list, models.TradeDealDetail{
			DealId:      *row.DealId,
			DealName:    *row.DealName,
			ZoneId:      *row.ZoneId,
			GoodsNum:    *row.GoodsNum,
			Creater:     *row.Creater,
			CreatTime:   *row.CreatTime,
			OverdueTime: *row.OverdueTime,
			EndTime:     *row.EndTime,
			Status:      *row.Status,
			Description: *row.Description,
			Price:       *row.Price,
			InstanceIds: ids,
		})
	}
	return
}

func (r *redis) QueryTaskList() (list []models.TaskDetail, err error) {
	request := qredis.NewDescribeTaskListRequest()
	request.Limit = common.Int64Ptr(100)
	request.Offset = common.Int64Ptr(0)
	if err != nil {
		return
	}
	response, err := r.client.DescribeTaskList(request)
	for _, row := range response.Response.Tasks {
		list = append(list, models.TaskDetail{
			JobId:        *row.TaskId,
			TaskType:     *row.TaskType,
			TaskStatus:   strconv.FormatFloat(*row.Progress, 'E', -1, 64),
			InstanceName: *row.InstanceName,
			InstanceId:   *row.InstanceId,
			StartTime:    *row.StartTime,
			EndTime:      *row.EndTime,
		})
	}
	return
}

// ResetPassword 重置redis实例密码
func (r *redis) ResetPassword(id string, passwd string) (err error) {
	request := qredis.NewResetPasswordRequest()
	request.Password = common.StringPtr(passwd)
	request.InstanceId = common.StringPtr(id)
	_, err = r.client.ResetPassword(request)
	return
}

// UpgradeInstance redis实例配置升级
func (r *redis) UpgradeInstance(id string, args models.RedisRequest) (err error) {
	request := qredis.NewUpgradeInstanceRequest()
	request.InstanceId = common.StringPtr(id)
	request.MemSize = common.Uint64Ptr(args.MemSize)
	if args.RedisReplicasNum >= 1 {
		int64Str := strconv.FormatInt(args.RedisReplicasNum, 10)
		strUint64, _ := strconv.ParseUint(int64Str, 0, 64)
		request.RedisReplicasNum = common.Uint64Ptr(strUint64)
	}
	if args.RedisShardNum >= 1 {
		int64Str := strconv.FormatInt(args.RedisShardNum, 10)
		strUint64, _ := strconv.ParseUint(int64Str, 0, 64)
		request.RedisShardNum = common.Uint64Ptr(strUint64)
	}
	_, err = r.client.UpgradeInstance(request)
	return
}

func (r *redis) Get(id string) (result models.RedisResponse, err error) {
	list, err := r.query(map[string]string{"id": id})
	if len(list) > 0 {
		result = list[0]
	}
	return
}

func (r *redis) ListByName(name string) (list []models.RedisResponse, err error) {
	return r.query(map[string]string{"name": name})
}

func (r *redis) ListByVpcId(vpcId string) (list []models.RedisResponse, err error) {
	return r.query(map[string]string{"vpcId": vpcId})
}

func (r *redis) ListAll() (list []models.RedisResponse, err error) {
	return r.query(map[string]string{})
}

func (r *redis) query(args map[string]string) (list []models.RedisResponse, err error) {
	request := qredis.NewDescribeInstancesRequest()
	request.Limit = common.Uint64Ptr(100)
	request.Offset = common.Uint64Ptr(0)
	if val, ok := args["id"]; ok {
		request.InstanceId = common.StringPtr(val)
	}
	if val, ok := args["name"]; ok {
		request.InstanceName = common.StringPtr(val)
	}
	if val, ok := args["vpcId"]; ok {
		request.UniqVpcIds = common.StringPtrs([]string{val})
	}
	response, err := r.client.DescribeInstances(request)
	if err != nil {
		return
	}
	for _, row := range response.Response.InstanceSet {
		instance := models.RedisResponse{
			InstanceName:     *row.InstanceName,
			InstanceId:       *row.InstanceId,
			Appid:            *row.Appid,
			RegionId:         *row.RegionId,
			ZoneId:           *row.ZoneId,
			Status:           *row.Status,
			PrivateIP:        *row.WanIp,
			Port:             *row.Port,
			Createtime:       *row.Createtime,
			Size:             *row.Size,
			Type:             *row.Type,
			AutoRenewFlag:    *row.AutoRenewFlag,
			DeadlineTime:     *row.DeadlineTime,
			Engine:           *row.Engine,
			ProductType:      *row.ProductType,
			UniqVpcId:        *row.UniqVpcId,
			UniqSubnetId:     *row.UniqSubnetId,
			BillingMode:      *row.BillingMode,
			InstanceTitle:    *row.InstanceTitle,
			OfflineTime:      *row.OfflineTime,
			SubStatus:        *row.SubStatus,
			RedisShardSize:   *row.RedisShardSize,
			RedisShardNum:    *row.RedisShardNum,
			RedisReplicasNum: *row.RedisReplicasNum,
			PriceId:          *row.PriceId,
			CloseTime:        *row.CloseTime,
		}
		if row.WanAddress != nil {
			instance.WanAddress = *row.WanAddress
		}
		list = append(list, instance)
	}
	return
}

// InquiryPrepaidPrice 查询redis包年包月价格
func (r *redis) InquiryPrepaidPrice(args models.RedisRequest) (result models.ItemPrice, err error) {
	return r.inquiryPrice(1, args)
}

// InquiryPostpaidPrice 查询redis按量付费价格
func (r *redis) InquiryPostpaidPrice(args models.RedisRequest) (result models.ItemPrice, err error) {
	return r.inquiryPrice(0, args)
}

// inquiryPrice 查询价格
func (r *redis) inquiryPrice(chargeType int64, args models.RedisRequest) (result models.ItemPrice, err error) {
	request := qredis.NewInquiryPriceCreateInstanceRequest()
	request.TypeId = common.Uint64Ptr(args.TypeId)
	request.MemSize = common.Uint64Ptr(args.MemSize)
	request.GoodsNum = common.Uint64Ptr(args.GoodsNum)
	if chargeType == 1 {
		request.Period = common.Uint64Ptr(args.Period)
	} else {
		request.Period = common.Uint64Ptr(1)
	}
	if r.isClusterType(args.TypeId) {
		request.RedisShardNum = common.Int64Ptr(args.RedisShardNum)
	}
	request.BillingMode = common.Int64Ptr(chargeType)
	request.RedisReplicasNum = common.Int64Ptr(args.RedisReplicasNum)
	request.ZoneName = common.StringPtr(args.ZoneName)
	request.ProductVersion = common.StringPtr(args.ProductVersion)
	response, err := r.client.InquiryPriceCreateInstance(request)
	if err != nil {
		return
	}
	price := response.Response.Price
	if price != nil {
		result.DiscountPrice = *price / 100.0
		result.OriginalPrice = *price / 55.0
	}
	return
}
